/*
 * graphshare.c
 *
 * Define and store a graph, with its multiple views, to be shared by modules.
 * Provide getters and setters. Correctness of provided additional structures
 * is not checked. These additional structures should be created with functions
 * provided by graphstrct.c
 *
 *  Created on: Aug 20, 2011
 *      Author: correa
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "graph.h"

#define GSH_SET_GRAPH_CODE 		0x0100
#define GSH_SET_NEIG_CODE		0x0200
#define GSH_SET_ANTINEIG_CODE	0x0400

// used to get or set graph
static adjMatrix 	GSH_g;

static int verticesordered = 0;

// re-ordering of vertices
static int * 		GSH_vorder;
static int  		GSH_vordsz = 0;
// anti-neighborhood
static int ** 		GSH_antineig = NULL;
static int * 		GSH_antineigsz;
// neighborhood
static int ** 		GSH_neig;
static int * 		GSH_neigsz;

// read a graph from a specified file, which is formated according to the DIMACS benchmark (see pargo-instances package)
// the local graph vector is initialized and filled with the graph read
// graphFile: file in the DIMACS benchmark format
// t: adjacency matrix is filled with the graph or with its complement
// n: the number of vertices is returned in this variable
// n: the number of edges is returned in this variable
int loadSharedGraph(FILE * graphFile, const graphType t) {
	if (hasGraph())
		return GSHERR_GRAPH_HAS_BEEN_LOADED_ALREADY;

	char               	type  = ' ';
	char               	linestr[100];
	char *             	datastr;
	int                	i, j;
	int 				n;
	long long 			m;

	n = 0;
	m = 0;

	while (type != 'p') {
		type = fgetc(graphFile);
		if (type != EOF) {

			/* header */
			if (type == 'c') {
				datastr = fgets(linestr, 100, graphFile);
				if (datastr != NULL)
					printf("%s", linestr);
				else
					return -1;
			}

			/* Vertices */
			if (type == 'p') {
				datastr = fgets(linestr, 100, graphFile);
				if (datastr == NULL)
					return -1;

				datastr = strtok(linestr," ");

				datastr = strtok(NULL," ");
				n = atoi(datastr);

				datastr = strtok(NULL," ");
				m = atoll(datastr);
				if (t == GT_COMPLEMENT)
					m = ((((long long) (n))*((long long) (n)) - ((long long) n)) >> ((long long) 1)) - m;
			}
		}
	}

	////
	// Graph variables
	////

	printf("Graph with %d vertices and %lld edges.\n", n, m);
	newAdjMatrix(n);

	if (t != GT_GRAPH) {
		addAllEdges();
		for (i = 0; i < n; i++)
			delEdge(i, i);
		m = nedges() >> 1;
	}

	type = fgetc(graphFile);
	while (type != EOF) {
		/* Edges */
		if (type == 'e') {
			datastr = fgets(linestr, 100, graphFile);
			if (datastr == NULL)
				return -1;

			datastr = strtok(linestr," ");
			i = atoi(datastr) - 1;

			datastr = strtok(NULL," ");
			j = atoi(datastr) - 1;

			if (t == GT_GRAPH) {
				addEdge(i, j);
				addEdge(j, i);
				(m)++;
			}
			else {
				delEdge(i, j);
				delEdge(j, i);
				(m)--;
			}
		}
		else {
			datastr = fgets(linestr, 100, graphFile);
			if (datastr != NULL)
				printf(" %s\n", linestr);
			else
				return -1;
		}
		type = fgetc(graphFile);
	}

	if ((nedges() >> 1) != m)
		printf("Ops! Reading %lld edges instead of %lld announced.\n", nedges() >> 1, m);

	getAdjMatrix(&GSH_g);
	return GSH_SET_GRAPH_CODE;
}

const adjMatrix * getSharedGraph() {
	if (!hasGraph())
		return NULL;
	getAdjMatrix(&GSH_g);
	return &GSH_g;
}

int setSharedGraph(const adjMatrix * adj) {
	if (hasGraph())
		return GSHERR_GRAPH_HAS_BEEN_LOADED_ALREADY;

	setAdjMatrix((adjMatrix *) adj);

	return GSH_SET_GRAPH_CODE;
}

int releaseSharedGraph(int code) {
	if (code == GSH_SET_GRAPH_CODE) {
		GSH_g.g = NULL;
		GSH_g.logrowsize = 0;
		GSH_g.matrixsize = 0;
		GSH_g.nedges = 0;
		GSH_g.nverts = 0;
		setAdjMatrix(&GSH_g);
		GSH_vorder = NULL;

		releaseAntiNeig(GSH_SET_ANTINEIG_CODE);
		releaseNeig(GSH_SET_NEIG_CODE);

		return 0;
	}
	return GSHERR_CODE_NOT_VALID;
}

const int * getSharedOrder() {
	return GSH_vorder;
}

int getSharedOrderSize() {
	return GSH_vordsz;
}

int setSharedOrder(int * const o, const int r, int code) {
	if (code != GSH_SET_GRAPH_CODE)
		return GSHERR_CODE_NOT_VALID;
	if (verticesordered)
		return GSHERR_VERTICES_IN_ORDER_ALREADY;
	verticesordered = 1;
	GSH_vorder = o;
	GSH_vordsz = r;
	return 0;
}

const int * getAntiNeig(int i) {
	return GSH_antineig ? GSH_antineig[i] : NULL;
}

const int getAntiNeigSize(int i) {
	return GSH_antineigsz ? GSH_antineigsz[i] : 0;
}

// may be used with a = NULL and sz > 0 to only set antineig size
int setAntiNeig(int * const a, int i, int sz) {
	if (!GSH_antineig)
		GSH_antineig = (int **) calloc(nverts(),sizeof(int *));
	if (!GSH_antineigsz)
		GSH_antineigsz = (int *) calloc(nverts(),sizeof(int));

	if (!GSH_antineig[i])
		return GSHERR_ANTINEIGHBORHOOD_HAS_BEEN_SET_ALREADY;

	GSH_antineig[i] = a;
	GSH_antineigsz[i] = sz;
	return GSH_SET_ANTINEIG_CODE;
}

int releaseAntiNeig(int code) {
	if (code == GSH_SET_ANTINEIG_CODE) {
		GSH_antineig = NULL;
		return 0;
	}
	return GSHERR_CODE_NOT_VALID;
}

const int * getNeig(int i) {
	return GSH_neig ? GSH_neig[i] : NULL;
}

const int getNeigSize(int i) {
	return GSH_neigsz ? GSH_neigsz[i] : 0;
}

// may be used with a = NULL and sz > 0 to only set neig size
int setNeig(int * const a, int i, int sz) {
	if (!GSH_neig)
		GSH_neig = (int **) calloc(nverts(),sizeof(int *));
	if (!GSH_neigsz)
		GSH_neigsz = (int *) calloc(nverts(),sizeof(int));

	if (!GSH_neig[i])
		return GSHERR_NEIGHBORHOOD_HAS_BEEN_SET_ALREADY;

	GSH_neig[i] = a;
	GSH_neigsz[i] = sz;
	return GSH_SET_NEIG_CODE;
}

int releaseNeig(int code) {
	if (code == GSH_SET_NEIG_CODE) {
		GSH_neig = NULL;
		return 0;
	}
	return GSHERR_CODE_NOT_VALID;
}
